package org.fujene.communicate.local;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.fujene.index.IndexingSystem;
import org.fujene.memman.LocalMemoryManager;
import org.fujene.search.SearchSystem;
import org.fujene.structure.Enumerates;
import org.fujene.structure.IndexQueue;
import org.fujene.structure.Result;

/**
 * Peer to Peerサービスです。 ここが全サービスの本体となります。
 */
public class LocalPeerSystem {
	// MemoryManager -> Memory, Disk
	private LocalMemoryManager memman = null;

	public boolean stopFlag = false;

	private ExecutorService execIndex = null;
	private ExecutorService execSearch = null;

	private String directory = null;
	private int blockNum = 0;
	private int blockSize = 0;

	private boolean debugFlag = false;

	// //////// P2P ///////////
	private static final int INDEX_NOREP = 2;

	// //////// Logger ///////////
	private Logger mainLogger = null;

	private boolean getArgs(String[] args) {
		boolean isEnded = false;
		String settingFile;

		if (args.length > 2 || args.length == 1) {
			System.err
					.println("Usage: PeerSystem [--setting <SettingFile>] [--secondary <IPAddress ...>]");
			return isEnded;
		}
		// デフォルト値
		this.blockNum = 256;
		this.blockSize = 1048576;
		this.directory = "./fujene_data/";
		this.mainLogger = Logger.getLogger("Fujene search engine");

		if (args.length > 1 && args[0].equals("--setting")) {
			settingFile = args[1];

			BufferedReader br = null;
			String input = null;
			try {
				br = new BufferedReader(new FileReader(new File(settingFile)));

				while ((input = br.readLine()) != null) {
					// "#"始まりはコメント行
					if (!input.startsWith("#")) {
						String[] settingArgs = input.split("=");
						if (settingArgs.length >= 2) {
							if (settingArgs[0].equalsIgnoreCase("BlockNum"))
								blockNum = Integer.parseInt(settingArgs[1]);
							else if (settingArgs[0]
									.equalsIgnoreCase("BlockSize"))
								blockSize = Integer.parseInt(settingArgs[1]);
							else if (settingArgs[0]
									.equalsIgnoreCase("Directory"))
								directory = settingArgs[1];
							else if (settingArgs[0]
									.equalsIgnoreCase("DebugMode")) {
								if (settingArgs[1].equalsIgnoreCase("on"))
									debugFlag = true;
							} else if (settingArgs[0]
									.equalsIgnoreCase("LogLevel")) {
								try {
									mainLogger.setLevel(Level
											.parse(settingArgs[1]));
								} catch (Exception e) {
									e.printStackTrace();
									mainLogger
											.severe("Specified log level does not exist.");
								}
							}
						}
					}

					isEnded = true;
				}
			} catch (FileNotFoundException e) {
				System.err
						.println("Please input appropriate file. Search system will shutdown.");
				return isEnded;
			} catch (IOException e) {
			} finally {
				if (br != null) {
					try {
						br.close();
					} catch (IOException e) {
					}
				}
			}
		} else if (args.length == 0) {
			isEnded = true;
		}

		return isEnded;
	}

	public void initialize() throws IOException, InterruptedException {
		new SearchSystem(this, mainLogger);

		new IndexingSystem(this, mainLogger);

		memman = new LocalMemoryManager(blockSize, blockNum, directory, false);

		execIndex = Executors.newFixedThreadPool(1);

		execSearch = Executors.newFixedThreadPool(1);

		memman.debugFlag = debugFlag;
	}

	// ローカルで動かしたいときはこっち
	public static void main(String[] args) throws InterruptedException,
			IOException {
		LocalPeerSystem mainSystem = new LocalPeerSystem();

		if (!mainSystem.getArgs(args))
			return;

		mainSystem.initialize();

		mainSystem.console();
	}

	private void console() throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String arg;

		try {
			Thread.sleep(400);
		} catch (InterruptedException ignored) {
		}

		while (true) {
			System.out.println("Queue for index: "
					+ ((ThreadPoolExecutor) execIndex).getActiveCount()
					+ " Queue for search: "
					+ ((ThreadPoolExecutor) execSearch).getActiveCount());

			System.out.print("Fujene > ");
			arg = br.readLine();

			String[] args = arg.split(" ");
			if (args.length > 0) {
				if (args[0].equals("index") || args[0].equals("i")) {
					if (args.length == 1)
						System.out.println("Specify string to index.");
					else {
						setNewIndex(new IndexQueue(Enumerates.INDEX, args[1]));
					}
				} else if (args[0].equals("search") || args[0].equals("s")) {
					if (args.length == 1)
						System.out.println("Specify query.");
					else {
						try {
							Result result;
							result = setNewSearch(new IndexQueue(
									Enumerates.SEARCH, args[1], 0, 100, 1L));
							// ちょっとなら結果を出す
							if (result.results.length <= 10) {
								for (int i = 0; i < result.results.length; i++) {
									if (result.results[i].length() > 0)
										System.out.println(i + ":\t"
												+ result.results[i]);
								}
							}
						} catch (Exception ignored) {
						}
					}
				} else if (args[0].equals("searchspec") || args[0].equals("p")) {
					if (args.length != 4)
						System.out.println("Specify arguments.");
					else {
						try {
							Result result = setNewSearch(new IndexQueue(
									Enumerates.SEARCH, args[1],
									Integer.parseInt(args[2]),
									Integer.parseInt(args[3]), 1L));
							System.out
									.println("Result total: "
											+ (result.results != null ? result.results.length
													: 0));
							if (result.results.length <= 10) {
								for (int i = 0; i < result.results.length; i++) {
									if (result.results[i].length() > 0)
										System.out.println(i + ":\t"
												+ result.results[i]);
								}
							}
						} catch (Exception ignored) {
							ignored.printStackTrace();
						}
					}
				} else if (args[0].equals("indexcount") || args[0].equals("c")) {
					System.out.println("Total count: " + memman.getCounter());
				} else if (args[0].equals("querycount") || args[0].equals("u")) {
					System.out.println("Total count: "
							+ memman.getSearchcounter());
				} else if (args[0].equals("termcount") || args[0].equals("t")) {
					System.out.println("Term count: " + memman.getTerms());
				} else if (args[0].equals("skipcount") || args[0].equals("k")) {
					System.out.println("Skip ptr. count: "
							+ memman.getSkipPointerLen());
				} else if (args[0].equals("memory") || args[0].equals("m")) {
					memman.getMemoryList();
				} else if (args[0].equals("save") || args[0].equals("v")) {
					saveMemory();
				} else if (args[0].equals("read") || args[0].equals("r")) {
					readMemory();
				} else if (args[0].equals("quit") || args[0].equals("q")) {
					setStopper(true);
				} else {
					System.out
							.println("Command: <i>ndex [args], <s>earch [args], searchs<p>ec [args] [from] [length], <t>ermcount, index<c>ount, q<u>erycount, s<k>ipcount, <m>emory, <r>ead, sa<v>e, <q>uit");
				}
			}
		}
	}

	// 外部からJUnitとかで呼び出すときはこっち
	public void testMain(String[] args) throws InterruptedException,
			IOException {
		this.blockNum = 32;
		this.blockSize = 1048576;
		this.directory = "./testDir/";
		this.initialize();

		memman.debugFlag = true;
	}

	// 全部の動作を止める操作
	public void setStopper(boolean isStop) {
		stopFlag = isStop;
		execIndex.shutdownNow();
		execSearch.shutdownNow();
		// 無理やりな気がする
		Runtime.getRuntime().exit(0);
	}

	// 大元のindexing窓口 いずれ@deprecated
	public void setNewIndex(IndexQueue newIndex) {
		newIndex.indexType = INDEX_NOREP;
		execIndex.execute(new PeerIndexContentsHost(newIndex));
	}

	public class PeerIndexContentsHost implements Runnable {
		private IndexQueue newIndex;

		public PeerIndexContentsHost(IndexQueue index) {
			this.newIndex = index;
		}

		@Override
		public void run() {
			try {
				memman.index(newIndex);
			} catch (UnsupportedEncodingException e) {
			}
		}
	}

	// 大元のsearch窓口
	public Result setNewSearch(IndexQueue newQuery) throws Exception {
		return memman.search(newQuery);
	}

	public void readMemory() {
		memman.initializeMemory();
	}

	public void saveMemory() {
		memman.saveMemory();
	}
}
